home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 07 - 1991 / 07.08 Aug 91 / Dots Source / cDotsDoc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-11-18  |  17.6 KB  |  634 lines  |  [TEXT/KAHL]

  1. /************************************
  2.     cDotsDoc.c
  3.         
  4.     SUPERCLASS = CDocument
  5.     
  6. Methods for the Dots Document. The document has one window. The window 
  7. containts a scrollable dots pane where the matrix is displayed and a 
  8. non-scrolling score pane which displays the score and current player turn.
  9.  
  10.  ************************************/
  11.  
  12. #include <CApplication.h>
  13. #include <CBartender.h>
  14. #include <CDataFile.h>
  15. #include <CDecorator.h>
  16. #include <CDesktop.h>
  17. #include <CError.h>
  18. #include <Commands.h>
  19. #include <Constants.h>
  20. #include <CPanorama.h>
  21. #include <CPrinter.h>
  22. #include <CScrollPane.h>
  23. #include <TBUtilities.h>
  24.  
  25. #include "cDotsDoc.h"
  26. #include "cDotsPane.h"
  27. #include "cScorePane.h"
  28. #include "dotsTypes.h"
  29.  
  30. /*** Class Constants ***/
  31. #define DLOG_PRINT        2000        /* Dialog to display when printing    */
  32. #define kBoxFree    0                    /* Initial box state */
  33. #define kDotsTop            44            /* Top of Dots Pane in window coord */
  34. #define kScoreBottom        (kDotsTop)    /* Bottom of Score Pane in window coord */
  35. #define strABORTPRINT    9            /* String index in STRcommon */
  36. #define WINDDots            128        /* Resource ID for WIND template */
  37.  
  38. /*** Globals ***/
  39. extern    CApplication *gApplication;/* The application */
  40. extern    CBartender    *gBartender;    /* The menu handling object */
  41. extern    CDecorator    *gDecorator;    /* Window dressing object    */
  42. extern    CDesktop        *gDesktop;        /* The enclosure for all windows */
  43. extern    OSType        gSignature;        /* The application's signature */
  44. extern    CError        *gError;            /* The global error handler */
  45.  
  46. /********** C O N S T R U C T I O N  ***********/
  47.  
  48. /*** IDotsDoc
  49. *
  50. * The document's initialization method.
  51. * If the document has its own instance variables, initialize them here.
  52. * At least invoke the default method.
  53. */
  54. void cDotsDoc::IDotsDoc(CBureaucrat *aSupervisor, Boolean printable)
  55. {
  56.     int            row;
  57.     int            col;
  58.     
  59.     CDocument::IDocument(aSupervisor, printable);
  60.     
  61.     fPlayerTurn = kPlayer1;    /* player 1 goes first */
  62.  
  63.         /* erase all lines in grid and clear the boxes */
  64.     for (row = 0; row <= kMaxRow; row++)
  65.     {
  66.         for (col = 0; col <= kMaxCol; col++)
  67.         {
  68.             fLines[hLine][row][col] = false;
  69.             fLines[vLine][row][col] = false;
  70.             fBoxes[row][col] = kBoxFree;
  71.         }
  72.     }
  73.  
  74. }
  75.  
  76.  
  77. /********** F I L E **********/
  78.  
  79. /*** NewFile    {OVERRIDE}
  80. *
  81. * When the user chooses New from the File menu, the CreateDocument()
  82. * method in the Application class will send a newly created document
  83. * this message. This method needs to create a new window, ready to
  84. * work on a new document.
  85. *
  86. * Since this method and the OpenFile() method share the code for creating
  87. * the window, an auxiliary window-building method is used.
  88. */
  89. void cDotsDoc::NewFile()
  90. {
  91.     Str255    wTitle;        /* Window title string    */
  92.     short        wCount;        /* Index number of new window    */
  93.     Str63        wNumber;        /* Index number as a string */
  94.  
  95.          /* Create new window, nothing to display yet so pass null */
  96.      BuildWindow(NULL);
  97.     
  98.         /* Append an index number to the    default name of the window    */
  99.     itsWindow->GetTitle(wTitle);
  100.     wCount = gDecorator->GetWCount();
  101.     NumToString((long)wCount, wNumber);
  102.     ConcatPStrings(wTitle, (StringPtr) "\p-");
  103.     ConcatPStrings(wTitle, wNumber);
  104.     itsWindow->SetTitle(wTitle);
  105.     
  106.     itsWindow->Select();        /* make it the active window */
  107. }
  108.  
  109.  
  110. /*** OpenFile    {OVERRIDE}
  111. *
  112. * When Open… is chosen from the File menu, the OpenDocument()
  113. * method in the Application class will let the user choose a file
  114. * and then send a newly created document this message. The information
  115. * about the file is in the SFReply record.
  116. *
  117. * In this method, open the file and display its contents
  118. * in a window. This method uses the auxiliary window-building method.
  119. */
  120. void cDotsDoc::OpenFile(SFReply *macSFReply)
  121. {
  122.     CDataFile    *theFile;
  123.     Handle        theData;
  124.     Str63        theName;
  125.     OSErr        theError;
  126.     
  127.         /* Create a file and send it a SFSpecify() message to
  128.         **    set up the name, volume, and directory */
  129.     theFile = new(CDataFile);
  130.     theFile->IDataFile();
  131.     theFile->SFSpecify(macSFReply);
  132.     
  133.         /*    Set the instance variable so other methods can use the file if
  134.         ** they need to. If you close the file after reading it, you
  135.         **    should be sure to set itsFile to NULL. */
  136.     itsFile = theFile;
  137.  
  138.     theError = theFile->Open(fsRdWrPerm);    /* Send file an Open() message */
  139.  
  140.         /* Check for open error. If error occured, CheckOSError
  141.         **    reports the error in an alert and returns false.
  142.         **    The default error message displays the error number.
  143.         **  Use Estr resources to customize the error message. */
  144.     if (!gError->CheckOSError(theError)) {
  145.         Dispose();    /* Get rid of object since file can't be opened */
  146.         return;
  147.     }
  148.  
  149.         /* Read in the data - don't use rainy day fund, its ok to fail */
  150.     gApplication->RequestMemory(FALSE, TRUE);
  151.     theFile->ReadAll(&theData);        /* ReadAll() creates the handle */
  152.     gApplication->RequestMemory(FALSE, FALSE);    /* set back to normal */
  153.  
  154.  
  155.         /* If not enough memory to open,
  156.         ** post error (should be -108) and get rid of ourselves. */
  157.     if (theData == NULL) {
  158.         gError->CheckOSError(MemError());
  159.         Dispose();
  160.         return;
  161.     }
  162.     
  163.         /* Use null to build window because dot and
  164.         ** score pane's draw routines do actual display of data */
  165.     BuildWindow(NULL);
  166.  
  167.     storeData(theData);    /* Store data as instance variables in document class. */
  168.     DisposHandle(theData);
  169.  
  170.         /* Put file name into window title */
  171.     itsFile->GetName(theName);
  172.     itsWindow->SetTitle(theName);
  173.     itsWindow->Select();            /* Don't forget to make the window active */
  174.     ((CDirector*)itsWindow->itsSupervisor)->Activate();
  175.         /* Select only activates if application was in background!! */
  176. }
  177.  
  178.  
  179. /*** BuildWindow
  180. *
  181. *    This is the auxiliary window-building method that the
  182. *    NewFile() and OpenFile() methods use to create a window.
  183. *
  184. *    In this implementation, the argument is the data to display.
  185. */
  186. void cDotsDoc::BuildWindow (Handle theData)
  187. {
  188.     CScrollPane    *theScrollPane;
  189.     Rect        r;
  190.     
  191.         /* Create window */
  192.     itsWindow = new(CWindow);
  193.     itsWindow->IWindow(WINDDots, FALSE, gDesktop, this);
  194.         /* set windows max and min sizes */
  195.     itsWindow->GetInterior(&r);
  196.     SetRect(&r, MIN_WSIZE, MIN_WSIZE, r.right - r.left, r.bottom - r.top);
  197.     itsWindow->SetSizeRect(&r);
  198.  
  199.         /* create a scroll pane and place it in the window */
  200.     theScrollPane = new(CScrollPane);
  201.     theScrollPane->IScrollPane(itsWindow, this, 0, 0, 0, 0,
  202.             sizELASTIC, sizELASTIC, TRUE, TRUE, TRUE);
  203.     theScrollPane->FitToEnclFrame(TRUE, TRUE);
  204.         /* Leave space at top of window for score pane, it does not scroll */
  205.      SetRect(&r, 0, kDotsTop, 0, 0);
  206.      theScrollPane->ChangeSize(&r, FALSE);
  207.     
  208.         /* Create dots pane and place it inside the scroll pane */
  209.     fDotsPane = new(cDotsPane);
  210.     fDotsPane->IDotsPane(theScrollPane, this, 0, 0, 0, 0, sizELASTIC, sizELASTIC);
  211.          /* Fit dots pane to interior of scroll pane.
  212.          ** Interior of scroll pane excludes scroll bars */
  213.      fDotsPane->FitToEnclosure(TRUE, TRUE);
  214.          /* Bounds were initialized to 0's so must set bounds to new frame size */
  215.      fDotsPane->GetFrameSpan(&r.right, &r.bottom);
  216.      SetRect(&r, 0, 0, r.right, r.bottom);
  217.      fDotsPane->SetBounds(&r);
  218.          /* Put dots pane in panorama */
  219.     theScrollPane->InstallPanorama(fDotsPane);
  220.     itsGopher = fDotsPane;    /* Dots pane will receive most of the clicks */
  221.     
  222.         /* Create score pane in the window */
  223.     fScorePane = new(cScorePane);
  224.     fScorePane->IScorePane(itsWindow, this, 0, 0, 0, 0, sizELASTIC, sizFIXEDTOP);
  225.     fScorePane->FitToEnclFrame(TRUE, TRUE);
  226.         /*  Position above dots pane */
  227.     fScorePane->GetFrame(&r);
  228.      SetRect(&r, 0, 0, 0, kScoreBottom - r.bottom);
  229.      fScorePane->ChangeSize(&r, FALSE);
  230.         
  231.         /* Allow for staggered window placement (NOTE: If this routine is to 
  232.         ** be used at all, it must come after subviews are placed in window
  233.         ** or strange things happen) */
  234.     gDecorator->PlaceNewWindow(itsWindow);
  235. }
  236.  
  237.  
  238. /*** DoSave {OVERRIDE}
  239. *
  240. * This method handles what happens when Save is chosen from the
  241. * File menu. This method should return TRUE if the file save was successful.
  242. * If there is no file associated with the document, send a
  243. * DoSaveFileAs() message.
  244. */
  245. Boolean cDotsDoc::DoSave()
  246. {
  247.     OSErr        errCode;
  248.     long        theLength;
  249.     long        totalLength;
  250.     int        x;
  251.     
  252.     if (itsFile == NULL)
  253.         return(DoSaveFileAs());
  254.     else
  255.     {
  256.             /* Go to beginning of file */
  257.         errCode = ((CDataFile *)itsFile)->SetMark(0, fsFromStart);
  258.         if (errCode != noErr)
  259.             return(gError->CheckOSError(errCode));
  260.                 
  261.             /* write player turn */
  262.         if (fPlayerTurn == kPlayer1)
  263.             x = 1;
  264.         else
  265.             x = 2;
  266.         errCode = ((CDataFile*)itsFile)->WriteSome((Ptr)&x, 2L);
  267.         if (errCode != noErr)
  268.             return(gError->CheckOSError(errCode));
  269.         totalLength = 2;
  270.             
  271.             /* write line states */
  272.         theLength = sizeof(tLines);
  273.         errCode = ((CDataFile*)itsFile)->WriteSome((Ptr)fLines, theLength);
  274.         if (errCode != noErr)
  275.             return(gError->CheckOSError(errCode));
  276.         totalLength += theLength;
  277.         
  278.             /* write box states */
  279.         theLength = (long)sizeof(tBoxes);
  280.         errCode = ((CDataFile*)itsFile)->WriteSome((Ptr)fBoxes, theLength);
  281.         if (errCode != noErr)
  282.             return(gError->CheckOSError(errCode));
  283.         totalLength += theLength;
  284.     
  285.             /* Set file length in case previous contents was bigger    */
  286.         errCode = ((CDataFile *)itsFile)->SetLength(totalLength);
  287.         if (errCode == noErr)        /* Force write to disk    */
  288.             errCode = FlushVol("", itsFile->volNum);
  289.         if (errCode != noErr)    
  290.             return(gError->CheckOSError(errCode));
  291.  
  292.         dirty = FALSE;                    /* Document is no longer dirty    */
  293.         gBartender->DisableCmd(cmdSave);
  294.         return(TRUE);                    /* Save was successful    */
  295.     }
  296. }
  297.  
  298.  
  299. /*** DoSaveAs {OVERRIDE}
  300. *
  301. * This method handles what happens when Save As… is chosen from
  302. * File menu. The default DoCommand() method for documents sends a DoSaveFileAs()
  303. * message which displays a standard put file dialog and sends this message.
  304. * The SFReply record contains all the information about the file you're about
  305. * to create.
  306. */
  307. Boolean cDotsDoc::DoSaveAs(SFReply *macSFReply)
  308. {
  309.         /* Close already open file and release its memory */ 
  310.     if (itsFile != NULL)
  311.         itsFile->Dispose();    
  312.  
  313.         /* Display standard file dialog and create new file */
  314.     itsFile = new(CDataFile);
  315.     ((CDataFile *)itsFile)->IDataFile();
  316.     itsFile->SFSpecify(macSFReply);
  317.     itsFile->CreateNew(gSignature, 'DATA');
  318.     itsFile->Open(fsRdWrPerm);
  319.  
  320.         /* Set window title to new file name */
  321.     itsWindow->SetTitle(macSFReply->fName);
  322.  
  323.     return( DoSave() );    /* save normally */
  324. }
  325.  
  326.  
  327. /*** DoRevert {OVERRIDE}
  328. *
  329. * Close the current file (without writing anything out)
  330. * and read the last saved version of the file.
  331. */
  332. void cDotsDoc::DoRevert()
  333. {
  334.     Point        homePos;
  335.     Handle    theData;
  336.     
  337.         /* Make sure file is open. If file is closed after an OpenFile()
  338.         ** then some other method must be used to store file info */
  339.     if (itsFile == NULL)
  340.         return;    /* need an error message here */
  341.  
  342.         /* Close file and reopen */
  343.     itsFile->Close();
  344.     if (!gError->CheckOSError(itsFile->Open(fsRdWrPerm)))
  345.         return;
  346.  
  347.     /* &&& Dispose of any current (in memory) contents of the document    */
  348.  
  349.         /* Get rid of last undo */
  350.     if (lastTask != NULL) {
  351.         lastTask->Dispose();
  352.         lastTask = NULL;
  353.     }
  354.         
  355.         /* Read in the data */
  356.     gApplication->RequestMemory(FALSE, TRUE);        /* Don't tap fund, can fail */
  357.     ((CDataFile *)itsFile)->ReadAll(&theData);    /* ReadAll() creates the handle */
  358.     gApplication->RequestMemory(FALSE, FALSE);    /* set back to normal */
  359.  
  360.         /* Check to see if data was read */
  361.     if (theData == NULL) {
  362.         gError->CheckOSError(MemError());
  363.         Dispose();
  364.         return;
  365.     }
  366.     
  367.         /* Store the data into the document */
  368.     storeData(theData);
  369.     DisposHandle(theData);    /* Don't need this anymore */
  370.     
  371.         /* set panoramas back to home position                */
  372.     ((CPanorama*)fDotsPane)->GetHomePosition(&homePos);
  373.     ((CPanorama*)fDotsPane)->ScrollTo(homePos, FALSE);
  374.     
  375.         /* Force redraw of panes on update */
  376.     fDotsPane->Refresh();
  377.     fScorePane->Refresh();
  378.         
  379.     dirty = FALSE;
  380. }
  381.  
  382.  
  383. /*** storeData
  384. *
  385. * Store the file data as as instance variables in the document
  386. */
  387. void cDotsDoc::storeData(Handle theData)
  388. {
  389.     int        x;
  390.     long    count;
  391.  
  392.     HLock(theData);
  393.             /* read whose turn it is */
  394.         BlockMove(*theData, &x, 2);
  395.         if (x == 1)
  396.             fPlayerTurn = kPlayer1;
  397.         else
  398.             fPlayerTurn = kPlayer2;
  399.         
  400.             /* read the line states */
  401.         x = sizeof(tLines);
  402.         count = (long)x;
  403.         BlockMove(*theData+2, fLines, count);
  404.         
  405.             /* read the box states */
  406.         count = (long)sizeof(tBoxes);
  407.         BlockMove(*theData+x+2, fBoxes, count);
  408.     HUnlock(theData);
  409. }
  410.  
  411.  
  412. /******* P R I N T I N G ********/
  413.  
  414. /*** PageCount {OVERRIDE}
  415. *
  416. * Return the number of pages in a Document
  417. */
  418. short    cDotsDoc::PageCount()
  419. {
  420.     long    pixWidth;
  421.     long    pixHeightDots;
  422.     long    pixHeightScore;
  423.     
  424.         /* Get dimensions of dots pane */
  425.     fDotsPane->GetPixelExtent(&pixWidth, &pixHeightDots);
  426.         /* Get dimensions of score pane */
  427.     fScorePane->GetPixelExtent(&pixWidth, &pixHeightScore);
  428.         /* Pages have a fixed pixel height */
  429.     return((pixHeightDots + pixHeightScore) / pageHeight + 1);
  430. }
  431.     
  432.  
  433. /*** AboutToPrint    {OVERRIDE}
  434. *
  435. * The specified range of pages is about to be printed, display print banner
  436. * and let the panes know they are about to be printed.
  437. */
  438. void    cDotsDoc::AboutToPrint(
  439.     short        *firstPage,
  440.     short        *lastPage)
  441. {
  442.     Str255        docName;
  443.     
  444.     inherited::AboutToPrint(firstPage, lastPage);
  445.     
  446.         /* Let the panes know they're supposed to print */
  447.     fDotsPane->AboutToPrint(firstPage, lastPage);
  448.     fScorePane->AboutToPrint(firstPage, lastPage);
  449.     
  450.         /* Display banner to let user know printing is taking place     */
  451.     PositionDialog('DLOG', DLOG_PRINT);
  452.     printBanner = GetNewDialog(DLOG_PRINT, NULL, (WindowPtr) -1L);
  453.     DrawDialog(printBanner);
  454.     
  455.         /* Document name shown in status box when printing to a
  456.         ** LaserWriter is taken from the    title of the top window,
  457.         ** which    is now our dialog box */
  458.     GetName(docName);                    
  459.     SetWTitle(printBanner, docName);
  460. }
  461.     
  462.  
  463. /*** PrintPageOfDoc {OVERRIDE}
  464. *
  465. * Print a single page of the document. This method must NOT send
  466. * messages to the Printer object. At this time, the print driver
  467. * is open and waiting for information to be drawn. Requests for
  468. * status information, such as page specifications, will foul up
  469. * the state of the print driver.
  470. */
  471. void    cDotsDoc::PrintPageOfDoc(short pageNum)
  472. {
  473.     fDotsPane->PrintPage(pageNum, pageWidth, pageHeight);
  474.     fScorePane->PrintPage(pageNum, pageWidth, pageHeight);
  475. }
  476.     
  477.  
  478. /***    DonePrinting {OVERRIDE}
  479. *
  480. * Print loop has been completed so remove print banner and let panes
  481. * know they are done printing.
  482. */
  483. void    cDotsDoc::DonePrinting()
  484. {
  485.     short        itemType;
  486.     Handle    item;
  487.     Rect        box;
  488.     Str63        abortStr;
  489.     long        ticks;
  490.     
  491.         /* If print job aborted display abort message */
  492.     if (PrError() == iPrAbort) {
  493.         GetDItem(printBanner, 1, &itemType, &item, &box);
  494.         GetIndString(abortStr, STRcommon, strABORTPRINT);
  495.         SetIText(item, abortStr);
  496.         Delay(120, &ticks);
  497.     }
  498.         
  499.     DisposDialog(printBanner);    /* Get rid of print banner */
  500.  
  501.         /* Let the panes know that printing is finished */
  502.     fDotsPane->DonePrinting();
  503.     fScorePane->DonePrinting();
  504.     
  505.         /* Changing the cursor back to an arrow after printing is 
  506.         ** normally accomplished through the main event loop. However,
  507.         ** when printing multiple documents from the finder  control does
  508.         ** not return to the main event loop between print jobs and 
  509.         ** succeeding print dialogs were using the left over watch cursor.
  510.         ** The following statement was used to correct this. */
  511.     SetCursor(&arrow);
  512. }
  513.  
  514.  
  515. /********* G A M E ***********/
  516.  
  517. /** getBoxState
  518. *
  519. * Get state of a box. 3 or less means not yet completed, 4 means completed
  520. *      by player 1, 5 means completed by player 2
  521. */
  522. tBoxState cDotsDoc::getBoxState(int row, int col)
  523. {
  524.     return(fBoxes[row][col]);
  525. }
  526.  
  527.  
  528. /*** changeBoxState
  529. *
  530. * Change state of box
  531. */
  532. Boolean cDotsDoc::changeBoxState(int row, int col,
  533.             Boolean thePlayer, Boolean addingLine)
  534. {
  535.     tBoxState    boxState;
  536.     Boolean        changed;
  537.     int            delta;
  538.     
  539.     changed = false;    /* assume box ownership did not changed */
  540.     boxState = fBoxes[row][col];    /* get old state */
  541.     
  542.     if (addingLine) {
  543.     
  544.             /* Is box completed? */
  545.         if (boxState < kBoxPlayer1) {
  546.             boxState = boxState + 1;
  547.             if (boxState == kBoxPlayer1) {
  548.                 changed = true;    /* box ownership changed */
  549.             
  550.                     /* Did player 2 complete box? */
  551.                 if (thePlayer == kPlayer2)
  552.                     boxState = kBoxPlayer2;
  553.                     
  554.                     /* Box completed, so change box and score */
  555.                 fDotsPane->invalBox(row, col);
  556.                 fScorePane->invalScore(thePlayer);
  557.             }
  558.         }
  559.     } else {    /* Remove line */
  560.             /* If box was completed redraw box and score */
  561.         if (boxState >= kBoxPlayer1) {
  562.             changed = true;    /* box ownership changed */
  563.             fDotsPane->invalBox(row, col);
  564.             fScorePane->invalScore(thePlayer);
  565.         }
  566.         
  567.             /* Update state of box */
  568.         if (boxState == kBoxPlayer2)
  569.             boxState = 3;
  570.         else
  571.             boxState = boxState - 1;
  572.     }
  573.     fBoxes[row][col] = boxState;    /* Change data structure */
  574.     return changed;
  575. }
  576.  
  577.  
  578. /*** getLineState
  579. *
  580. * Get state of line. True means there is a line in direction from
  581. * the input dot, false no line
  582. */
  583. tLineState cDotsDoc::getLineState(tLineDir direction, int row, int col)
  584. {
  585.     return(fLines[direction][row][col]);
  586. }
  587.  
  588.  
  589. /*** setLineState
  590. *
  591. * Change state of line. Set to true for a line or false for no line
  592. */
  593. void cDotsDoc::setLineState(tLineDir direction, int row, int col,
  594.             Boolean thePlayer, tLineState newState)
  595. {
  596.     Boolean    aChange, bChange;
  597.     Boolean    oldState;
  598.     
  599.     aChange = bChange = false;    /* Assume box ownership did not change */
  600.     oldState = getLineState(direction, row, col);
  601.     if (oldState != newState) {
  602.         fLines[direction][row][col] = newState;
  603.         fDotsPane->invalLine(direction, row, col); /* must redraw line */
  604.         
  605.             /* Change state of any box to right or under line */
  606.         if ((row < kMaxRow) && (col < kMaxCol))
  607.             aChange = changeBoxState(row, col, thePlayer, newState);
  608.         
  609.             /* Change state of any box to left or above line */
  610.         if (direction == hLine) {
  611.             if (row > 0)
  612.                 bChange = changeBoxState(row-1, col, thePlayer, newState);
  613.         } else {
  614.             if (col > 0)
  615.                 bChange = changeBoxState(row, col-1, thePlayer, newState);
  616.         }
  617.         
  618.             /* If box ownership did not change it is other player's turn */
  619.         if (!aChange && !bChange) {
  620.             fPlayerTurn = !fPlayerTurn;            /* Let other player have turn */
  621.             fScorePane->setTurn(fPlayerTurn);    /* Make score view show whose turn it is */
  622.         }
  623.     }
  624. }
  625.  
  626.  
  627. /*** getPlayerTurn
  628. *
  629. * Identify player whose move it is
  630. */
  631. Boolean    cDotsDoc::getPlayerTurn()
  632. {
  633.     return(fPlayerTurn);
  634. }